﻿using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.IO;
using System.Xml;
using System.Xml.Xsl;

public class ContentHandler : IHttpHandler {

    public const string NamespaceQS = "http://schemas.altairis.cz/XML4web/Arguments#QueryString";
    public const string NamespaceFF = "http://schemas.altairis.cz/XML4web/Arguments#FormFields";

    public bool IsReusable {
        get { return true; }
    }

    public void ProcessRequest(HttpContext context) {
        // Get name of XML data file and load it
        string xmlFileName = context.Server.MapPath(ConfigurationManager.AppSettings["DataFileName"]);
        if (!File.Exists(xmlFileName)) {
            ShowErrorPage(context, 500, "Internal Server Error", "Data file not found.");
            return;
        }
        XmlDocument xmlDoc = new XmlDocument();
        try {
            xmlDoc.Load(xmlFileName);
        }
        catch (Exception ex) {
            ShowErrorPage(context, 500, "Internal Server Error", "Error loading data file: " + ex.Message);
            return;
        }

        // Get name of XSL template and load it
        string xslFileName = GetTemplateFileName(context);
        if (string.IsNullOrEmpty(xslFileName)) {
            ShowErrorPage(context, 404, "Object Not Found", "Invalid template name.");
            return;
        }
        XslCompiledTransform xslDoc = new XslCompiledTransform();
        try {
            xslDoc.Load(xslFileName);
        }
        catch (Exception ex) {
            ShowErrorPage(context, 500, "Internal Server Error", "Error loading template file: " + ex.Message);
            return;
        }

        // Prepare argument list
        XsltArgumentList args = new XsltArgumentList();
        foreach (string key in context.Request.QueryString.AllKeys) {
            if (!string.IsNullOrEmpty(key)) args.AddParam(key.ToLower(), NamespaceQS, context.Request.QueryString[key]);
        }
        foreach (string key in context.Request.Form.AllKeys) {
            if (!string.IsNullOrEmpty(key)) args.AddParam(key.ToLower(), NamespaceFF, context.Request.Form[key]);
        }

        // Append base URL of application and iframe script
        UriBuilder url = new UriBuilder(context.Request.Url);
        url.Query = string.Empty;
        url.Fragment = string.Empty;
        args.AddParam("MyUrl", string.Empty, url.ToString().TrimEnd('/'));
        url.Path = context.Request.ApplicationPath.Equals("/") ? string.Empty : context.Request.ApplicationPath;
        args.AddParam("BaseUrl", string.Empty, url.ToString().TrimEnd('/'));

        // Perform transformation
        try {
            xslDoc.Transform(xmlDoc, args, context.Response.OutputStream);
        }
        catch (Exception ex) {
            ShowErrorPage(context, 500, "Internal Server Error", "Error performing transformation: " + ex.Message);
            return;
        }

        // Setup cacheability
        int expiration = int.Parse(ConfigurationManager.AppSettings["Expiration"]);
        context.Response.AddFileDependency(xmlFileName);
        context.Response.AddFileDependency(xslFileName);
        context.Response.Cache.SetCacheability(HttpCacheability.ServerAndPrivate);
        context.Response.Cache.SetETagFromFileDependencies();
        context.Response.Cache.SetLastModifiedFromFileDependencies();
        context.Response.Cache.VaryByParams["*"] = true;
        context.Response.Cache.SetExpires(DateTime.Now.AddMinutes(expiration));
    }

    /// <summary>
    /// Gets the name of the template file.
    /// </summary>
    /// <param name="context">The current HTTP context.</param>
    /// <returns>The name of the template file corresponding to current request.</returns>
    private static string GetTemplateFileName(HttpContext context) {
        // Get template file name from called url (transform.*.axd)
        string[] pathData = Path.GetFileName(context.Request.PhysicalPath).Split('.');

        // Validate template name
        if (pathData.Length != 3 || !System.Text.RegularExpressions.Regex.IsMatch(pathData[1], "^[a-zA-Z0-9-_]{1,}$")) return null;

        // Check if corresponding file name exists
        string fileName = string.Format(ConfigurationManager.AppSettings["TemplateFileName"], pathData[1]);
        fileName = context.Server.MapPath(fileName);
        if (!File.Exists(fileName)) return null;
        return fileName;
    }

    /// <summary>
    /// Shows the error page.
    /// </summary>
    /// <param name="context">The current HTTP context.</param>
    /// <param name="httpStatusCode">The HTTP status code.</param>
    /// <param name="httpStatusDescription">The HTTP status description.</param>
    /// <param name="message">The message to display.</param>
    private static void ShowErrorPage(HttpContext context, int httpStatusCode, string httpStatusDescription, string message) {
        context.Response.Clear();
        context.Response.StatusCode = httpStatusCode;
        context.Response.StatusDescription = httpStatusDescription;
        context.Response.Output.WriteLine("<html><head><title>{0} {1}</title></head>", httpStatusCode, httpStatusDescription);
        context.Response.Output.WriteLine("<body><h1>{0} {1}</h1><p>{2}</body></html>", httpStatusCode, httpStatusDescription, message);
    }

}
